#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

struct newchardev_led
{
    int major;
    int minor;
    dev_t dev_id;

    struct cdev st_chardev_led;
    struct class *st_class_chardev_led;
    struct device* st_device_chardev_led;
};

struct newchardev_led st_newchardev_led;

#define CHARDEVLED_MAJOR       200
#define CHARDEVLED_NAME         "03_chardevled"

#define LED_ON      1
#define LED_OFF     0

#define CCM_CCGR1_BASE                             0x020C406C
#define SW_MUX_GPIO1_IO03_BASE         0x020E0068
#define SW_PAD_GPIO1_IO03_BASE          0x020E02F4
#define GPIO1_DR_BASE                                 0x0209C000
#define GPIO1_GDIR_BASE                             0x0209C004

static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

void SwitchLED(u8 state)
{
    u32 reg_val;

    if(state == LED_ON)
    {
        reg_val = readl(GPIO1_DR);
        reg_val &= ~(1 <<3);
        writel(reg_val,GPIO1_DR);
    }
    else if(state == LED_OFF)
    {
        reg_val = readl(GPIO1_DR);
        reg_val |= (1 <<3);
        writel(reg_val,GPIO1_DR);
    }
}

int led_open(struct inode *nd, struct file *fp)
{
    fp->private_data = &st_newchardev_led;
    return 0;
}

ssize_t led_read(struct file *fp, char __user *buf, size_t len, loff_t *offset)
{
    return 0;
}


ssize_t led_write (struct file *fp, const char __user *buf, size_t len, loff_t *offset)
{

    int retval = 0;
    unsigned char recvbuf[1];
    unsigned char led_state;
    retval = copy_from_user(recvbuf,buf,len);
    if(retval < 0)
    {
        printk("kernel write failed\r\n");
        return -EFAULT;
    }

    led_state = recvbuf[0];

    if (led_state == LED_ON)        
    {
        SwitchLED(LED_ON);
    }
    else if (led_state == LED_OFF)
    {
        SwitchLED(LED_OFF);
    }

    return 0;
}

int led_release (struct inode *nd, struct file *fp)
{
    return 0;
}

struct file_operations  led_fops =  
{
        .owner = THIS_MODULE,
        .open = led_open,
        .read = led_read,
        .write = led_write,
        .release = led_release,
};

static int __init newchardevled_init(void)
{
    u32 reg_val;

    //寄存器地址映射
    IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE,4);
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE,4);
    SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE,4);
    GPIO1_DR = ioremap(GPIO1_DR_BASE,4);
    GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE,4);

    //使能GPIO1时钟
    reg_val = readl(IMX6U_CCM_CCGR1);
    reg_val &= ~(3<<26);
    reg_val |= (3<<26);
    writel(reg_val,IMX6U_CCM_CCGR1);

    //设置复用功能为GPIO 
    writel(5,SW_MUX_GPIO1_IO03);

    //设置IO 属性
    writel(0x10B0,SW_PAD_GPIO1_IO03);

    //设置为输出功能
    reg_val = readl(GPIO1_GDIR);
    reg_val &= ~(1<<3);
    reg_val |= (1<<3);
    writel(reg_val,GPIO1_GDIR);

    //默认关闭GPIO 
    reg_val = readl(GPIO1_DR);
    reg_val |= (1<<3);
    writel(reg_val,GPIO1_DR);

    
    if(st_newchardev_led.major)
    {
        //int register_chrdev_region(dev_t, unsigned, const char *);
        
        st_newchardev_led.dev_id = MKDEV(st_newchardev_led.major,0);
        register_chrdev_region(st_newchardev_led.dev_id,1,CHARDEVLED_NAME);
    }
    else
    {
        //alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
        alloc_chrdev_region(&st_newchardev_led.dev_id,0,1,CHARDEVLED_NAME);
        st_newchardev_led.major = MAJOR(st_newchardev_led.dev_id);
        st_newchardev_led.minor = MINOR(st_newchardev_led.dev_id);
    }
    printk("major:%d\tminor:%d\r\n",st_newchardev_led.major,st_newchardev_led.minor);


    //通过结构体c_dev注册字符设备
    st_newchardev_led.st_chardev_led.owner = THIS_MODULE;
    //void cdev_init(struct cdev *, const struct file_operations *);
    cdev_init(&st_newchardev_led.st_chardev_led,&led_fops);
    //int cdev_add(struct cdev *, dev_t, unsigned);
    cdev_add(&st_newchardev_led.st_chardev_led,st_newchardev_led.dev_id,1);

    //创建设备节点
    //1.创建一个class
    //2.在class下面创建一个设备

    /*
    struct class * __must_check __class_create(struct module *owner,
						  const char *name,
						  struct lock_class_key *key);
    返回一个结构体指针
    */
   
    st_newchardev_led.st_class_chardev_led = class_create(THIS_MODULE,CHARDEVLED_NAME);
    if (IS_ERR(st_newchardev_led.st_class_chardev_led))
    {
        return PTR_ERR(st_newchardev_led.st_class_chardev_led);
    }
    
    /*
    struct device *device_create(struct class *cls, struct device *parent,
			     dev_t devt, void *drvdata,
			     const char *fmt, ...);
    返回一个结构体指针             
    */
   
    st_newchardev_led.st_device_chardev_led = device_create(st_newchardev_led.st_class_chardev_led,NULL,st_newchardev_led.dev_id,NULL,CHARDEVLED_NAME);
    if (IS_ERR(st_newchardev_led.st_device_chardev_led))
    {
        return PTR_ERR(st_newchardev_led.st_device_chardev_led);
    }
    
    return 0;
}


static void __exit newchardevled_exit(void)
{
    iounmap(IMX6U_CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);
    //void cdev_del(struct cdev *);
    cdev_del(&st_newchardev_led.st_chardev_led);
    //void unregister_chrdev_region(dev_t, unsigned);
    unregister_chrdev_region(st_newchardev_led.dev_id,1);

    device_destroy(st_newchardev_led.st_class_chardev_led,st_newchardev_led.dev_id);
    class_destroy(st_newchardev_led.st_class_chardev_led);
}

module_init(newchardevled_init);
module_exit(newchardevled_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lux");
